#include "communication.h"
#include "datamanager.h"
#include "rsacrypto.h"
#include "taskqueue.h"
#include <QThread>
#include <QSharedMemory>
#include <QDateTime>
#include <QCryptographicHash>
#include <QDebug>

Communication::Communication(Message& msg, QObject *parent) : m_msgInfo(msg), QObject{parent}
{
    setAutoDelete(true);
}

Communication::~Communication()
{
    if (m_aes) {
        delete m_aes;
        m_aes = nullptr;
    }
}

// 生成对称加密AES的密钥
QByteArray Communication::generateAesKey(KeyLen keylength)
{
    // 为了保证密钥的随机性，可以通过获取当前的系统时间来生成密钥
    QByteArray time = QDateTime::currentDateTime().toString("yyyy.MM.dd-hh:mm:ss.zzz").toUtf8();
    // 进行哈希计算
    QCryptographicHash hash(QCryptographicHash::Sha3_384);
    hash.addData(time);
    time = hash.result();
    // 选取keylength长度作为密钥
    time = time.left(keylength);

    return time;
}

// 接收非对称加密（RSA）的公钥，进行对称密钥(AES)的分发（分发RSA的公钥时没有对数据进行加密操作，只有进行了数字签名）
void Communication::handleRsaFenFa(Message *msg)
{
    RsaCrypto rsa;
    // 将公钥从Message结构体里面解析出来
    // data1 : Rsa的公钥  data2 : 数字签名
    rsa.parseStringToKey(msg->data1, RsaCrypto::PublicKey);
    // 校验数字签名
    bool flag = rsa.verify(msg->data2, msg->data1);
    if (!flag) {
        qDebug() << "Failed to verify the digital signature......";
        return ;
    }
    assert(flag);
    // 生成对称加密(AES)的密钥
    m_aesKey = generateAesKey(KeyLen::L32);

    // TestCode TODO: ??????
    // m_aesKey = "1234567887654321";
    // QByteArray testdata = "人生若只如初见，何事秋风悲画扇。";
    // testdata = m_aes->encrypt(testdata);
    // qDebug() << "加密之后的密文: " << testdata;
    // testdata = m_aes->decrypt(testdata);
    // qDebug() << "解密之后的明文: " << testdata;


    // 分发AES的密钥时，需要先用RSA的公钥对其进行加密。同时，计算AES密钥的哈希值，用于服务器对客户端的发送数据的校验（分发RSA的公钥时，没有对数据进行加密）
    Message resMsg;
    resMsg.reqcode = RequestCode::AesFenFa;
    resMsg.data1 = rsa.pubKeyEncrypt(m_aesKey); // 用RSA公钥加密的AES密钥
    // qDebug() << "Communication::handleRsaFenFa::resMsg.data1 = " << resMsg.data1.data();
    resMsg.data2 = QCryptographicHash::hash(m_aesKey, QCryptographicHash::Sha224).toHex(); // 16进制数字串格式的哈希散列值
                                                                                           // 为了让服务端校验客户端的数据
    sendMessage(&resMsg, false);
}

// 给服务器发送数据
void Communication::sendMessage(Message *msg, bool encrypt)
{
    // 对数据进行序列化
    Codec codec(msg);
    QByteArray data = codec.encodeMsg();

    // 用AES的密钥加密数据
    if (encrypt) {
        // 对序列化之后的数据进行加密(加密之前必须要获取到对称加密的密钥)
        data = m_aes->encrypt(data);
        qDebug() << "Communication::sendMessage 用AES加密之后的密文:" << data.data();
    }
    // 在对称加密密钥进行分发时，不会进行对称加密


    m_socket->sendMsg(data);
}

// 解析数据 ------ 解析服务器发送过来的数据
void Communication::parseRecvMessage()
{
    // 接收服务端发送的数据
    QByteArray data = m_socket->recvMsg(2);
    qDebug() << "client has receive data from server......";
    qDebug() << "Communication::parseRecvMessage::data = " << data.size();
    if (data.isEmpty()) {
        return ;
    }
    // 将服务端发送来的数据进行反序列化得到加密的数据
    Codec codec(data);
    QSharedPointer<Message> ptr = codec.decodeMsg();
    qDebug() << "ptr->rescode = " << (int)ptr->rescode << ", ptr->data1 = " << ptr->data1 << ", ptr->data2 = " << ptr->data2;
    switch (ptr->rescode) {
    case RespondCode::LoginOk:
        emit loginOk();
        qDebug() << "User login was successful......";
        break;
    case RespondCode::RegisterOk:
        emit registerOk();
        qDebug() << "User registration was successful......";
        break;
    case RespondCode::RsaFenFa:
        handleRsaFenFa(ptr.get());
        break;
    case RespondCode::AesVerifyOk:
        qDebug() << "The Aes key was successfully distributed.";
        m_aes = new AesCrypto(AesCrypto::AES_CBC_256, m_aesKey);

        qDebug() << "Communication::parseRecvMessage::已建立连接------发送的注册之后的原始数据: m_msgInfo.userName =" << m_msgInfo.userName
                 << " m_msgInfo.reqcode = " << m_msgInfo.reqcode
                 << " 哈希之后的passwd = " << m_msgInfo.data1
                 << " telephone = " << m_msgInfo.data2;

        sendMessage(&m_msgInfo);
        break;
    case RespondCode::JoinRoomOk:
        DataManager::getInstance()->setRoomName(ptr->roomName);
        // 信号的接收者在GameMode类的构造函数里面
        emit playerCount(ptr->data1.toInt());
        break;
    case RespondCode::DealCards:
        parseCards(ptr->data1, ptr->data2);
        break;
    case RespondCode::StartGame:
        emit startGame(ptr->data1);
        break;
    case RespondCode::Failed:
        emit failedMsg(ptr->data1);
        break;
    case RespondCode::OtherGrabLord:
    {
        Task t;
        t.bet = ptr->data1.toInt();
        TaskQueue::getInstance()->addTask(t);
        break;
    }
    case RespondCode::OtherPlayHand:
    {
        // data1是数量 data2是数据
        Task t;
        QDataStream stream(ptr->data2);
        for (int i = 0; i < ptr->data1.toInt(); ++ i) {
            Card c;
            stream >> c;
            t.cs.add(c);
        }
        TaskQueue::getInstance()->addTask(t);
        break;
    }
    case RespondCode::SearchRoomOk:
    {
        // 如果是true，就发射信号到搜索房间的窗口
        bool flag = (ptr->data1 == "true" ? true : false);
        emit roomExist(flag);
        break;
    }
    case RespondCode::OtherLeaveRoom:
    {
        int count = ptr->data1.toInt();
        emit somebodyLeave(count);
        break;
    }
    default:
        break;
    }
}

// 解析服务端发送过来的扑克牌的信息
void Communication::parseCards(QByteArray data1, QByteArray data2)
{
    // 服务端发送卡牌数据的约定规则: xx-xx#xx-xx#xx-xx#
    // 先去除服务端发送过来的卡牌数据的最后一个"#"
    auto func = std::bind([=](QByteArray msg) {
        auto lst = msg.left(msg.length() - 1).split('#');
        Cards cs;
        for (const auto& item : lst) {
            auto sub = item.split('-');
            Card card(static_cast<Card::CardPoint>(sub.last().toInt()),
                      static_cast<Card::CardSuit>(sub.first().toInt())
                      );
            cs.add(card);
        }
        return cs;
    }, std::placeholders::_1);

    Cards cards = func(data1);
    Cards last3Cards = func(data2);
    // 存储卡牌数据
    DataManager::getInstance()->setCards(cards, last3Cards);
}


void Communication::run()
{
    // m_socket = QSharedPointer<TcpSocket>::create(new TcpSocket());

    m_socket = new TcpSocket();
    // 连接服务器
    DataManager* obj = DataManager::getInstance();
    bool flag = m_socket->connectToHost(obj->getIp(), obj->getPort().toInt());
    qDebug() << "======The child thread of communication id:" << QThread::currentThreadId() << "connected Status ?" << flag;
    if (!flag) {
        // 通知主线程连接失败
        emit connectFailed();
        delete m_socket;
        return ;
    }
    // 连接成功
    while (!m_stop) {
        /*
         * 处理服务器的回复数据:
         *                     1. 首先得到服务器回复的非对称加密的公钥
         *                     2. 客户端生成对称加密密钥并用非对称加密的公钥进行加密
         *                     3. 将加密之后的对称加密的密钥发送给服务器
         *                     4. 服务器接受客户端发送的密钥，通过非对称加密的私钥进行解密得到对称加密的密钥，并回复接受密钥成功的消息，
         *                     5. 客户端成功接收服务端的回复消息，并发送登录或者注册请求
         **/
        parseRecvMessage();
    }
    m_socket->disConnect();
    delete m_socket;
}
